import os
import sys
import glob
import fitz
from datetime import datetime
try:
    # 尝试从上层目录的 base_code 导入
    parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(parent_dir)
    from base_code import *
    from base_code.common import *
except ImportError:
    print("❌ 错误: 无法找到 'base_code' 模块。请确保 pocketflow 代码位于正确的路径。")


class CommandRegistry(ToolSystem):
    def __init__(self):
        super().__init__()
        self.lines = [
            "     欢迎使用 ReAct plus v0.4 (工作流版)",
            " ",
            "支持自然语言交互，所有逻辑由Agent的P-D-A循环由工作流处理",
            " ",
            "ls <path>                - 列出path目录中的文件列表",
            "cd <path>                - 改变当前目录",
            "pwd                      - 显示当前目录",
            "list_tools               - 显示工具列表",
            "exit                     - 退出程序"
        ]

    def get_tools_description(self) -> str:
        """为 LLM 生成格式化的工具描述列表。"""
        if not self._tools:
            return "  - 无可用工具。"
        lines = []
        for name, tool in self._tools.items():
            # 为 'finish' 等内置控制工具提供特殊描述
            if name == 'finish':
                lines.append(
                    "  - finish(answer: str): 当你完成所有任务并得出最终结论时调用，在 'answer' 中提供最终答案。")
                continue

            args_list = list(tool.required_args) + [f"{k}={v}" for k, v in (tool.optional_args or {}).items()]
            # 过滤掉内部参数 'memory'
            args_list = [arg for arg in args_list if arg != 'memory']
            args_str = ", ".join(args_list)
            lines.append(f"  - {tool.name}({args_str}): {tool.description}")
        return "\n".join(lines)

# ==============================================================================
# 全局单例对象：工具系统（环境层）
# ==============================================================================
GLOBAL_TOOL_SYSTEM = CommandRegistry()


# ==============================================================================
#  使用装饰器注册一个本地工具作为示例
# ==============================================================================
@tool(
    tool_system=GLOBAL_TOOL_SYSTEM,
    name="ls",
    description=(
            "列出指定目录中的文件和文件夹。\n"
            "参数:\n"
            "  - path (str, 可选): 目标目录路径或通配符模式。如果未提供，则使用当前目录 '.'。\n"
            "示例调用:\n"
            "  ls(path='.')  → 列出当前目录\n"
            "  ls(path='*.pdf')  → 列出当前目录下所有 PDF 文件\n"
            "  ls(path='C:/Users')  → 列出指定路径下的内容"
    ),
    required_args=[],
    optional_args={"args": "."}
)
def list_file(path="."):
    """
    列出匹配指定路径或模式的文件和目录，并将格式化的结果作为字符串返回。
    支持通配符，如 * 和 ?。
    """

    def get_visual_width(s: str) -> int:
        """计算字符串的视觉宽度，中文字符计为2，其他计为1。"""
        width = 0
        for char in s:
            if '\u4e00' <= char <= '\u9fff':
                width += 2
            else:
                width += 1
        return width
    def pad_str(s: str, width: int, align: str = 'left') -> str:
        """根据视觉宽度填充字符串。"""
        visual_width = get_visual_width(s)
        padding_size = width - visual_width
        if padding_size < 0: padding_size = 0
        padding = ' ' * padding_size
        if align == 'right':
            return padding + s
        else:
            return s + padding
    output_lines = []
    path_or_pattern = path
    # --- 核心逻辑：区分路径和模式 ---
    # 检查输入字符串是否包含通配符
    has_wildcard = '*' in path_or_pattern or '?' in path_or_pattern

    target_paths = []
    display_dir_path = ""
    listing_mode = ""  # 用于在输出中显示我们是如何操作的

    if not has_wildcard and os.path.isdir(path_or_pattern):
        # Case 1: 输入是一个明确的目录路径
        listing_mode = f"目录内容: {os.path.abspath(path_or_pattern)}"
        display_dir_path = path_or_pattern
        try:
            # os.listdir() 返回目录内的所有条目名称
            entries = os.listdir(path_or_pattern)
            # 我们需要完整的路径来获取 os.stat 信息
            target_paths = [os.path.join(path_or_pattern, entry) for entry in entries]
        except PermissionError:
            return f"错误：没有权限访问目录 '{path_or_pattern}'。"

    else:
        # Case 2 & 3: 输入是模式，或是一个不存在的路径（让glob处理）
        listing_mode = f"匹配模式: '{path_or_pattern}'"
        target_paths = glob.glob(path_or_pattern)
        # 对于通配符模式，显示其所在的目录
        display_dir_path = os.path.dirname(path_or_pattern) if os.path.dirname(path_or_pattern) else "."

    # --- 统一的格式化输出部分 ---
    if not target_paths:
        return f"在 '{os.path.abspath(display_dir_path)}' 中找不到与 '{path_or_pattern}' 匹配的文件或目录。"

    output_lines = []
    DATE_WIDTH, TIME_WIDTH, ATTR_WIDTH, SIZE_WIDTH = 12, 10, 8, 14

    output_lines.append(f"\n{listing_mode}\n")

    header = (
            pad_str('日期', DATE_WIDTH) +
            pad_str('时间', TIME_WIDTH) +
            pad_str('属性', ATTR_WIDTH) +
            pad_str('大小', SIZE_WIDTH, align='right') +
            ' ' + '名称'
    )
    output_lines.append(header)
    output_lines.append("-" * (get_visual_width(header) + 5))

    dir_count = 0
    file_count = 0
    total_size = 0

    # 遍历所有目标路径（无论是来自listdir还是glob）
    for path in sorted(target_paths):
        try:
            stat = os.stat(path)
            is_dir = os.path.isdir(path)
            attr = "D" if is_dir else " "
            mtime = datetime.fromtimestamp(stat.st_mtime)
            date_str = mtime.strftime("%Y/%m/%d")
            time_str = mtime.strftime("%H:%M")

            size_str = "<DIR>" if is_dir else f"{stat.st_size:,}"
            if not is_dir:
                total_size += stat.st_size

            # 只显示文件名部分，而不是完整路径
            display_name = os.path.basename(path)

            data_line = (
                    pad_str(date_str, DATE_WIDTH) +
                    pad_str(time_str, TIME_WIDTH) +
                    pad_str(attr, ATTR_WIDTH) +
                    pad_str(size_str, SIZE_WIDTH, align='right') +
                    ' ' + display_name
            )
            output_lines.append(data_line)

            if is_dir:
                dir_count += 1
            else:
                file_count += 1

        except (PermissionError, FileNotFoundError):
            display_name = os.path.basename(path)
            error_line = (
                    pad_str('', DATE_WIDTH) + pad_str('', TIME_WIDTH) + pad_str('', ATTR_WIDTH) +
                    pad_str('<无法访问>', SIZE_WIDTH, align='right') + ' ' + display_name
            )
            output_lines.append(error_line)
            continue

    separator = "-" * (get_visual_width(header) + 5)
    output_lines.append(separator)

    summary_str = f"{file_count} 个文件, {dir_count} 个目录, 总大小: {total_size:,} 字节"
    output_lines.append(summary_str)

    return "\n".join(output_lines)


@tool(
    tool_system=GLOBAL_TOOL_SYSTEM,
    name="pdf_read",
    description=(
            "读入pdf文件内容。\n"
            "参数:\n"
            "  - path (str, 必选): pdf文件路径。\n"
            "  - max_pages (int, 可选): 最多读入页数，默认为1。\n"
            "示例调用:\n"
            "  read_pdf(path='example.pdf')  → 读入example.pdf的第1页\n"
            "  read_pdf(path='example.pdf', max_pages=5)  → 读入example.pdf的1-5页"
    ),
    required_args=["path"],
    optional_args={"max_pages": 1}
)
def read_pdf(path: str, max_pages: Optional[int] = 1) -> dict:
    """读取 PDF 文件前 max_pages 页的文本内容"""
    if not os.path.exists(path):
        raise FileNotFoundError(f"文件 '{path}' 不存在。")
    if not path.lower().endswith(".pdf"):
        raise ValueError("文件必须是 PDF 格式。")

    doc = fitz.open(path)
    # 在关闭文档前，获取并保存 page_count
    total_pages = doc.page_count

    texts = []
    for i in range(min(total_pages, max_pages)):
        page = doc.load_page(i)
        texts.append(page.get_text("text"))

        # 2. 现在可以安全地关闭文档了
    doc.close()

    # 3. 返回时使用之前保存的变量
    return {"page_count": total_pages, "text": "\n".join(texts)}


async def main():
    result1 = await GLOBAL_TOOL_SYSTEM.run_tool("ls",  {"path": "."})
    result2 = await GLOBAL_TOOL_SYSTEM.run_tool("pdf_read", {"path": "r:/2425_42_10490_080201_21430219212132_BS_001.pdf", "max_pages": 1})
    print(result1)
    print(result2)

if __name__ == "__main__":
    asyncio.run(main())
